Data Import and Processing
These analyses pull data from production and development DHIS2 environments to assess client movement.
Data look like this:
#read file from production and view
raw4<-suppressMessages(read_csv("BrianDs.csv"))
#View(raw4)
#colnames(raw4)
head(raw4)
## # A tibble: 6 x 8
## EnrOrgUnit ENrUserType TeiUid StageUid VisitNo EvtOrgUnit EvtUserType GestAge
## <chr> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
## 1 uyaWnaBmAs1 HA rbodU~ Ty22Qt2~ 1 uyaWnaBmA~ HA 25.3
## 2 uyaWnaBmAs1 HA kkpNN~ Ty22Qt2~ 1 uyaWnaBmA~ HA 13.4
## 3 uyaWnaBmAs1 HA v14CD~ Ty22Qt2~ 1 uyaWnaBmA~ HA 19.4
## 4 uyaWnaBmAs1 HA vd7bT~ Ty22Qt2~ 1 uyaWnaBmA~ HA 25.1
## 5 uyaWnaBmAs1 HA trPZE~ Ty22Qt2~ 1 uyaWnaBmA~ HA 25.3
## 6 gtUM8Eraqvu HA TdT1e~ Ty22Qt2~ 1 gtUM8Eraq~ HA 21.4
Data from development environment
#now get background data from dev
#get Org Unit groups
baseurl<-"https://bd-eregistry.dhis2.org/dhis/"
username<-"ing_test"
#function for logging in
loginDHIS2<-function(baseurl,username,password) {
url<-paste0(baseurl,"api/me")
r<-GET(url,authenticate(username,password))
warn_for_status(r, task="log in")
if(r$status_code == 200L){return(TRUE)}
}
if(loginDHIS2(baseurl, username, password)==TRUE){
print("successfully logged in")
}else{
stop("could not log in! Please check url, username and password")
}
## [1] "successfully logged in"
#groups
url<-paste0(baseurl, "api/organisationUnitGroups.json?paging=false&fields=id,name,organisationUnits")
ou_groups<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>%
data.frame() %>%
select("name"=1,"id"=2,"members"=3) %>%
unnest_longer(members) %>%
flatten()
head(ou_groups)
## name id members.id
## 1 10/20/31/50 Bed Hospital (not UHC) iXQnUg4Ayjg <NA>
## 2 Baganbari Union, Uttar Mat qzxlprBwoN2 TNlLoHITukJ
## 3 Baganbari Union, Uttar Mat qzxlprBwoN2 qDuy7VCWXEu
## 4 Baganbari Union, Uttar Mat qzxlprBwoN2 Law6euHPccf
## 5 Baganbari Union, Uttar Mat qzxlprBwoN2 kKuwT6hXLqh
## 6 Baganbari Union, Uttar Mat qzxlprBwoN2 oKhlabKlBHa
#stages
url<-paste0(baseurl, "api/programStages.json?paging=false&fields=id,name")
ps<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>%
data.frame() %>%
select("id"=2, "psname"=1)
head(ps)
## id psname
## 1 piRv8jtcLQV ANC 1st visit_
## 2 WZbXY0S00lP ANC 1st visit
## 3 h6idWg9SfPr ANC Follow up sheet
## 4 ViHTJrKKrFg ANC Green File
## 5 tlzRiafqzgd ANCManagements
## 6 iXDSolqmauJ ANCRisks
#OU names
url<-paste0(baseurl, "api/organisationUnits.json?paging=false&fields=id,name")
ou_names<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>%
data.frame() %>%
select("name"=1, "ou_id"=2) %>%
mutate("ou_type"=case_when(
str_detect(name, " FWC") ~ "FWC",
str_detect(name, "(?i)CC") ~ "CC",
str_detect(name, "Unit") ~ "Unit",
str_detect(name, "Ward") ~ "Ward"))
head(ou_names)
## name ou_id ou_type
## 1 Aburkandi FWC mf3RMT3QRhf FWC
## 2 Aithadi Pacani CC, Matlab(North) si7FoHyJpZ0 CC
## 3 Amiapur Cc - Matlab(north) P1okfUePFEd CC
## 4 Aswinpur FWC ubVXGg2leZa FWC
## 5 BAGANBARI UNION, UTTAR MATLAB mR8aUxvBDgQ <NA>
## 6 BAHERCHOR LOTURDI CC PduJMfKZhFt CC
Data Processing
We then merge the raw outputs of TEI events with org unit information
If arrange events by visit number, classify subsequent visits as being at same or different org unit than the initial visit.
my_data<-raw4 %>%
left_join(ou_names, by=c("EvtOrgUnit"="ou_id")) %>%
select(tei=3, ga=8, "ou_id"=EvtOrgUnit, ou_type, name, VisitNo, EnrOrgUnit, StageUid) %>%
distinct() %>% #event must be unique visit, i.e. an ANC management and ANC stage same week would be merged
mutate(ga=round(as.numeric(ga))) %>%
filter(!is.na(ou_type) & !is.na(ga) & ga <= 50 & ga >= 1) %>%
mutate(VisitNo=if_else(VisitNo > 6, "7+", as.character(VisitNo))) %>%
mutate(VisitNo=as.factor(VisitNo)) %>%
mutate(ou_type=recode_factor(ou_type, "FWC"="FWC", "CC"="CC", "Ward"="Ward", "Unit"="Unit" )) %>%
group_by(tei) %>%
add_tally() %>%
arrange(tei, ga) %>%
mutate(first = dplyr::first(ou_id)) %>%
mutate(ga_initial = dplyr::first(ga)) %>%
mutate(last_ou = lag(ou_id)) %>%
mutate(Moved_ou = case_when(first == ou_id & is.na(last_ou) ~ "Event 1",
first == ou_id & !is.na(last_ou) ~ "Event 2+, same ou as Event 1",
first != ou_id ~ "Event 2+, different ou as Event 1")) %>%
mutate(Moved_ou_wrap = str_wrap(Moved_ou, width = 20))
#my_data
head(my_data)
## # A tibble: 6 x 14
## # Groups: tei [4]
## tei ga ou_id ou_type name VisitNo EnrOrgUnit StageUid n first
## <chr> <dbl> <chr> <fct> <chr> <fct> <chr> <chr> <int> <chr>
## 1 A0du~ 21 HWOI~ Ward Ward~ 1 HWOIuGUJV~ Ty22Qt2~ 1 HWOI~
## 2 a0lM~ 40 P1ok~ CC Amia~ 1 P1okfUePF~ Ty22Qt2~ 2 P1ok~
## 3 a0lM~ 40 P1ok~ CC Amia~ 1 P1okfUePF~ WZbXY0S~ 2 P1ok~
## 4 a0Mz~ 25 ZiEo~ Unit Unit~ 1 ZiEozn9m9~ Ty22Qt2~ 1 ZiEo~
## 5 a0RW~ 18 uMnk~ Ward Ward~ 1 uMnkQBPF2~ Ty22Qt2~ 2 uMnk~
## 6 a0RW~ 33 Sr8A~ FWC Shak~ 1 uMnkQBPF2~ WZbXY0S~ 2 uMnk~
## # ... with 4 more variables: ga_initial <dbl>, last_ou <chr>, Moved_ou <chr>,
## # Moved_ou_wrap <chr>
Visualization
Let’s start simple with a histogram. GA of each visit, by OU type.
p<-ggplot(my_data, aes(ga))+
geom_histogram(bins=25)+
facet_wrap(~ou_type, ncol=1)+
labs(title="Events by GA and OU type")
p

Density Plot by Org Unit type and Gestational Age
Another way to show this histogram is a density dot plot. To make it interesting we can animate it, to emphasize this progression of time. It looks a bit like a paint roller…
p<-ggplot(my_data, aes(ga, ou_type)) +
geom_jitter(aes(group = ga, size = .3), height = 0.25, show.legend = FALSE) +
labs(title="Pregnancy events in e-Reg Matlab by Org Unit",
subtitle = 'Visits at Gestational Age {closest_state}',
y = 'Org Unit Type') +
# scale_colour_manual(values = col_scale) +
transition_states(ga, transition_length = 3, state_length = 2) +
shadow_mark(size = .5) +
ease_aes('linear')
animate(
plot = p,
nframes = 200,
duration = 15,
end_pause = 50
)

But this doesnt say much about patient movement. When do clients move to a different org unit clinic?
The red dots are the GA and location (org unit) of first event (identification).
The blue dots are subsequent events that are at same location as first event.
The green dots are movement to a DIFFERENT location than the identification org unit.
p<-ggplot(my_data, aes(ga, Moved_ou_wrap)) +
geom_jitter(aes(group = ga, color = Moved_ou_wrap), size = 0.01) +
facet_wrap(~ou_type, ncol = 1)+
theme(legend.position = "none") +
labs(title="Pregnancy events in e-Reg Matlab by Org Unit") +
ylab("")
p

Same thing, but animated…
p<-p +
labs(title="Pregnancy events in e-Reg Matlab by Org Unit",
subtitle = 'Visits at Gestational Age {closest_state}') +
theme(legend.position = "none") +
transition_states(ga, transition_length = 3, state_length = 2) +
shadow_mark(size = .3) +
ease_aes('linear')
animate(
plot = p,
nframes = 200,
duration = 15,
end_pause = 50
)

Now we can see that most movement to new place happens after 36 weeks for FWA units and HA Wards (Home PPC follow up for those not identified by FWA). But women move to FWC between 14 and 35 weeks. Fewer women choose to move to CC, if they were identified elsewhere.
We want to narrow in on the patients who start at one org unit, and receive services at another. This approach tells us what kind of org unit they GO to, but not what kind of org unit they COME from.
Heat Map
If we narrow in on the patients who move to a new location (the green dots above), we can see the overlap of service provision between types of org unit.
Here is a table of events that are at a different org unit than the enrollment org unit, arranged by org unit type.
pcords_data <-my_data %>%
ungroup() %>%
filter(Moved_ou=="Event 2+, different ou as Event 1") %>%
rename("event_ou_id"=ou_id, "event_ou_type"=ou_type) %>%
left_join(ou_names, by=c("EnrOrgUnit"="ou_id")) %>%
rename("enr_ou_type"=ou_type) %>%
select(enr_ou_type, event_ou_type, "event_ga"=ga, "enr_ga"=ga_initial)
test<-pcords_data %>%
arrange(event_ou_type) %>%
mutate(event_ou_type=factor(event_ou_type, levels=c("CC","FWC","Unit","Ward"))) %>%
group_by(enr_ou_type, event_ou_type) %>%
select("enrollment OU"=enr_ou_type, "event OU"=event_ou_type) %>%
summarize(count=n())
kable(test) %>%
kable_styling()
|
enrollment OU
|
event OU
|
count
|
|
CC
|
CC
|
4
|
|
CC
|
FWC
|
29
|
|
CC
|
Unit
|
9
|
|
FWC
|
CC
|
11
|
|
FWC
|
FWC
|
8
|
|
FWC
|
Unit
|
10
|
|
Unit
|
CC
|
100
|
|
Unit
|
FWC
|
298
|
|
Unit
|
Unit
|
27
|
|
Unit
|
Ward
|
16
|
|
Ward
|
CC
|
14
|
|
Ward
|
FWC
|
73
|
|
Ward
|
Unit
|
4
|
We can visualize this graph in a heatmap
# Give extreme colors:
library(viridis)
ggplot(test, aes(`enrollment OU`, `event OU`, fill= count)) +
geom_tile() +
scale_fill_viridis(discrete=FALSE) +
theme_minimal()

We can facet these down by enrollment OU, then show the event GA for each subsequent event.
heat2<-pcords_data %>%
mutate(event_ou_type=factor(event_ou_type, levels=c("CC","FWC","Unit","Ward"))) %>%
mutate(gestage_event=case_when(
event_ga >= 0 & event_ga < 18 ~ "0-17",
event_ga >= 18 & event_ga < 24 ~ "18-23",
event_ga >= 24 & event_ga < 29 ~ "24-29",
event_ga >= 29 & event_ga < 34 ~ "29-33",
event_ga >= 34 & event_ga < 40 ~ "34-39",
event_ga >= 40 ~ "40+",
)) %>%
group_by(enr_ou_type, event_ou_type, gestage_event) %>%
select("enrollment OU"=enr_ou_type, "event OU"=event_ou_type, gestage_event) %>%
summarize(count=n())
ggplot(heat2, aes(gestage_event, `event OU`, fill= count)) +
geom_tile() +
scale_fill_viridis(discrete=FALSE) +
facet_wrap(~`enrollment OU`,labeller = "label_both")+
labs(title="Events at different OU than enrollment",
subtitle="By Event Gest Age and OU Type")

Animated Dot Plot
What are the patterns over time though?
In this animation, we focus on the “green dots” above. Each green dot is an event. The horizontal lines represent a type of org unit. The middle grey dots represent the identification events–once they cross that dot, the pregnancy is identified.
After that, clients go to many other types of org units. Some go to a different org unit of same type, while others go to a new org unit type.
By animating this over gestational age at visit, we can see which weeks had highest “crossover” of events. The speed of dot movement represents the time between visits.
pcord2<-pcords_data %>%
rownames_to_column(var="id") %>%
mutate("enr_start"=enr_ou_type) %>%
pivot_longer(c('enr_start', 'enr_ou_type','event_ou_type'),
names_to = "start_finish",
values_to="ou_type") %>%
mutate(gestage=if_else(start_finish=="enr_ou_type", enr_ga,
if_else(start_finish=="event_ou_type", event_ga, 0))) %>%
mutate(endpoint=if_else(start_finish=="enr_start", 0,
if_else(start_finish=="enr_ou_type", 1, 2))) %>%
mutate(ou_type=factor(ou_type, levels = c("Unit","Ward","FWC","CC"))) %>%
mutate(gestage=if_else(endpoint==2 & enr_ga > 30 & gestage > 30, gestage + 2, gestage)) %>%
mutate(gestage=if_else(endpoint==2, gestage + 1, gestage))
pcord_summ<-pcord2 %>%
filter(endpoint!=0) %>%
group_by(endpoint, ou_type, gestage) %>%
summarise("count"=n_distinct(id)) %>%
mutate("cumsum"=cumsum(count))
#pcord2 %>% filter(endpoint==2 & gestage > 35)
ps1<-pcord_summ %>% filter(endpoint==1)
ps2<-pcord_summ %>% filter(endpoint==2)
p2<-ggplot() +
geom_point(data = pcord2, aes(x=endpoint, y = ou_type,
group = id), col = "green") +
geom_point(data = ps1, aes(x=endpoint, y = ou_type, size = cumsum),
col="grey", alpha=0.8) +
geom_point(data = ps2, aes(x=endpoint, y = ou_type, size = cumsum),
col="grey", alpha=0.8) +
theme_minimal() +
transition_reveal(gestage) +
scale_x_continuous(breaks=c(0, 1, 2),
labels=c("GA 0","Identification", "Other Event")) +
labs(title="e-Reg Matlab -- Events at Different Org Unit than Identification",
subtitle = 'Events at Gestational Age {round(frame_along)}',
x = "")
animate(p2,
nframes = 200,
duration = 15,
end_pause = 50)

If a dot moves quickly between identification and subsequent visit, that means that the next visit happened quickly after identification (e.g., Unit identifies pregnancy at 16 weeks, visit to CHCP at 18 weeks). Inversely, the second visit may occur long after identification (same client visits CHCP again at 32 weeks, this would be a slower moving dot).
Eventually want to recreate as a Sankey flow diagram across 4 ANC visits. See below.
Parallel Coordinates
If we want to explore these relationships further, we can use interactive visualization.
The type below is called parallel coordinates.
The vertical axes represent variables. The horizontal and diagonal lines are observations, where each is an EVENT that took at a different place than the enrollment org unit. The colors are based on the type of enrollment org unit.
You can click and drag across an axis to select a range for each variable, and it will filter down to the observations that meet that criteria. For example, of those clients who were enrolled at a WARD, and later went to a CC, what was the range of gestational ages when that CC visit took place?
##### Parallel Coordinates Graph ######
library(parcoords)
#parallel coordinates with color based on gender
parcoords::parcoords(data = pcords_data,
rownames = TRUE,
color = list(
# discrete or categorical column
colorScale = "scaleOrdinal",
colorBy = "enr_ou_type",
colorScheme = "schemeCategory10"),
withD3 = TRUE,
brushMode = "1D-axes-multi",
alphaOnBrushed = 0.2,
queue = TRUE,
rate = 50,
reorderable = TRUE)
To make the correlations easier to read, you can drag and rearrange the axes order.
We might expand on this by linking to the selected ranges to tables, for dynamic filtering of data.
Dropouts: Isolating Clients with only one event
If the client has only one event in system, maybe they are different for some reason than the other events.
For example, what stage was their only event?
What kind of org unit?
The below tables are only one event.
First is by org unit – most of these are the Pregnancy ID stage.
library(kableExtra)
###Isolate those that only havd one event
my_data_iso<-raw4 %>%
left_join(ou_names, by=c("EvtOrgUnit"="ou_id")) %>%
select(tei=3, ga=8, "ou_id"=EvtOrgUnit, ou_type, name, VisitNo, EnrOrgUnit, StageUid) %>%
left_join(ps, by = c("StageUid"="id")) %>%
mutate(ga=round(as.numeric(ga))) %>%
filter(!is.na(ou_type) & !is.na(ga) &
ga <= 50 & ga >= 1 &
str_detect(psname, paste(c("regnanc", "ANC", "Newborn","PNC","Lab"),collapse = '|')) &
!str_detect(psname, paste(c("Prev","Risk","Manag"),collapse = '|'))) %>%
group_by(tei) %>%
add_tally() %>%
arrange(tei, ga) %>%
mutate(first = dplyr::first(ou_id)) %>%
mutate(last_ou = lag(ou_id)) %>%
ungroup() %>%
mutate(Moved_ou = case_when(first == ou_id & is.na(last_ou) ~ "Event 1",
first == ou_id & !is.na(last_ou) ~ "Event 2+, same ou as Event 1",
first != ou_id ~ "Event 2+, different ou as Event 1"))
# my_data_iso %>%
# group_by(ou_type, n) %>%
# summarise("events"= n()) %>%
# mutate(percent = round(events/sum(events), 2))
#my_data_iso
test2<-my_data_iso %>%
group_by(tei) %>%
filter(n==1) %>%
group_by(psname) %>%
summarize("stage_count"=n())
test3<-my_data_iso %>%
filter(n==1) %>%
group_by(ou_type) %>%
summarize("ou_type_count"=n())
#kableExtra::kable(test2)
kable(test2) %>%
kable_styling()
|
psname
|
stage_count
|
|
ANC 1st visit_
|
2
|
|
গর্ভকালীন সেবার তথ্য (Home - ANC record)
|
4
|
|
গর্ভাবস্থার সনাক্তকরণ (Pregnancy identification)
|
2775
|
|
ANC 1st visit
|
1
|
|
ANC visit
|
6
|
|
ANC visit_
|
2
|
Next is by org unit. Most of these are the FWA Units.
kable(test3) %>%
kable_styling()
|
ou_type
|
ou_type_count
|
|
CC
|
72
|
|
FWC
|
46
|
|
Unit
|
1887
|
|
Ward
|
785
|
Cascade by Visit Number
This section shows ALL VISITS for each patient We can visualize the visit number (x axis), GA at visit (time), and type of org unit at each visit (y axis). The inspiration is this NYTimes infographic
Each dot represents a patient as she moves through each level of the health system. The patient’s dot “rests” at the location of the last visit recorded. Note that the this considers each identification, ANC visit, or home visit stage a separate “visit”.
You can see that very few patients get past the 3rd visit at any level. Most of the migration occurs from the FWA Unit level up the system to FWC. And compared to CC level, more clients who made it to FWC by the third visit started from another org unit (red dots).
cascade_data <-my_data %>%
ungroup() %>%
rename("event_ou_id"=ou_id, "event_ou_type"=ou_type) %>%
select(tei, event_ou_type, "event_ga"=ga, Moved_ou, StageUid) %>%
left_join(ps, by = c("StageUid"="id")) %>%
filter(!is.na(event_ou_type) & !is.na(event_ga) &
event_ga <= 50 & event_ga >= 1 &
str_detect(psname, paste(c("regnanc", "ANC"),collapse = '|')) &
!str_detect(psname, paste(c("Prev","Risk","Manag", "Out"),collapse = '|'))) %>%
group_by(tei) %>%
mutate("VisitNo"=if_else(str_detect(psname, "ident"), 1, 2)) %>%
arrange(VisitNo, event_ga) %>%
mutate("VisitNo"=row_number())
cascade_data<-cascade_data %>%
mutate("VisitNo"=if_else(VisitNo==1, 0, as.double(VisitNo))) %>%
bind_rows(cascade_data %>% filter(VisitNo == 1)) %>%
mutate("Moved_ou"=if_else(VisitNo <=1, "Event 1", Moved_ou)) %>%
mutate(event_ou_type=factor(event_ou_type, levels = c("Unit","Ward","FWC","CC"))) %>%
mutate(groupid= group_indices()) %>%
arrange(tei, VisitNo) %>%
mutate(gestage=if_else(VisitNo==0, 0, event_ga)) %>%
filter(VisitNo<8) %>%
mutate(gestage=if_else(gestage==lag(gestage) & groupid==lag(groupid) & gestage!=0,
gestage+2, gestage))
p3<-ggplot() +
geom_jitter(data = cascade_data, aes(x=VisitNo, y = event_ou_type,
group = groupid, col = Moved_ou),
size=0.5, width = 0.15, height=0.1) +
theme_minimal() +
transition_reveal(gestage) +
scale_x_continuous(breaks=c(0:7),
labels=c("GA 0", "Event1", "Event2",
"Event3","Event4","Event5", "Event6","Event7"),
minor_breaks = NULL) +
scale_colour_manual(values = c("grey", "red", "darkblue")) +
labs(title="e-Reg Matlab -- Events 1-7",
subtitle = 'Events at Gestational Age {round(frame_along)}',
x = "",
y="")+
theme(legend.position = "bottom")
animate(p3,
nframes = 200,
duration = 15,
end_pause = 50)

#gganimate::anim_save("bd_visit1to7.gif")
LS0tDQp0aXRsZTogIkNsaWVudCBNb3ZlbWVudCBpbiBlLVJlZyBNYXRsYWI6IFZpc3VhbCBBbmFseXNpcyINCmF1dGhvcjogIkJyaWFuIE8nRG9ubmVsbCINCmRhdGU6ICI1LzgvMjAyMCINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpDQoNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdnYW5pbWF0ZSkNCmxpYnJhcnkoaHR0cikNCmxpYnJhcnkoanNvbmxpdGUpDQpsaWJyYXJ5KGFzc2VydHRoYXQpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHZpcmlkaXMpDQpwYXNzd29yZDwtcmVhZF9saW5lcygicGFzc3cudHh0IikNCg0KYGBgDQoNCiMgRGF0YSBJbXBvcnQgYW5kIFByb2Nlc3NpbmcNCg0KVGhlc2UgYW5hbHlzZXMgcHVsbCBkYXRhIGZyb20gcHJvZHVjdGlvbiBhbmQgZGV2ZWxvcG1lbnQgREhJUzIgZW52aXJvbm1lbnRzIHRvIGFzc2VzcyBjbGllbnQgbW92ZW1lbnQuDQoNCkRhdGEgbG9vayBsaWtlIHRoaXM6DQoNCmBgYHtyIHByb2QgZGF0YX0NCg0KI3JlYWQgZmlsZSBmcm9tIHByb2R1Y3Rpb24gYW5kIHZpZXcNCg0KcmF3NDwtc3VwcHJlc3NNZXNzYWdlcyhyZWFkX2NzdigiQnJpYW5Ecy5jc3YiKSkNCg0KI1ZpZXcocmF3NCkNCiNjb2xuYW1lcyhyYXc0KQ0KDQpoZWFkKHJhdzQpDQoNCmBgYA0KDQoNCkRhdGEgZnJvbSBkZXZlbG9wbWVudCBlbnZpcm9ubWVudA0KDQpgYGB7ciBkZXYgZGF0YX0NCiNub3cgZ2V0IGJhY2tncm91bmQgZGF0YSBmcm9tIGRldg0KI2dldCBPcmcgVW5pdCBncm91cHMNCmJhc2V1cmw8LSJodHRwczovL2JkLWVyZWdpc3RyeS5kaGlzMi5vcmcvZGhpcy8iDQp1c2VybmFtZTwtImluZ190ZXN0Ig0KDQoNCiNmdW5jdGlvbiBmb3IgbG9nZ2luZyBpbg0KbG9naW5ESElTMjwtZnVuY3Rpb24oYmFzZXVybCx1c2VybmFtZSxwYXNzd29yZCkgew0KICB1cmw8LXBhc3RlMChiYXNldXJsLCJhcGkvbWUiKQ0KICByPC1HRVQodXJsLGF1dGhlbnRpY2F0ZSh1c2VybmFtZSxwYXNzd29yZCkpDQogIHdhcm5fZm9yX3N0YXR1cyhyLCB0YXNrPSJsb2cgaW4iKQ0KICBpZihyJHN0YXR1c19jb2RlID09IDIwMEwpe3JldHVybihUUlVFKX0NCn0NCg0KDQppZihsb2dpbkRISVMyKGJhc2V1cmwsIHVzZXJuYW1lLCBwYXNzd29yZCk9PVRSVUUpew0KICBwcmludCgic3VjY2Vzc2Z1bGx5IGxvZ2dlZCBpbiIpDQp9ZWxzZXsNCiAgc3RvcCgiY291bGQgbm90IGxvZyBpbiEgUGxlYXNlIGNoZWNrIHVybCwgdXNlcm5hbWUgYW5kIHBhc3N3b3JkIikNCn0NCg0KI2dyb3Vwcw0KdXJsPC1wYXN0ZTAoYmFzZXVybCwgImFwaS9vcmdhbmlzYXRpb25Vbml0R3JvdXBzLmpzb24/cGFnaW5nPWZhbHNlJmZpZWxkcz1pZCxuYW1lLG9yZ2FuaXNhdGlvblVuaXRzIikNCm91X2dyb3VwczwtZnJvbUpTT04oY29udGVudChHRVQodXJsKSwgInRleHQiKSwgZmxhdHRlbiA9IFRSVUUpICU+JSANCiAgZGF0YS5mcmFtZSgpICU+JSANCiAgc2VsZWN0KCJuYW1lIj0xLCJpZCI9MiwibWVtYmVycyI9MykgJT4lIA0KICB1bm5lc3RfbG9uZ2VyKG1lbWJlcnMpICU+JSANCiAgZmxhdHRlbigpDQpoZWFkKG91X2dyb3VwcykNCiAgDQojc3RhZ2VzDQp1cmw8LXBhc3RlMChiYXNldXJsLCAiYXBpL3Byb2dyYW1TdGFnZXMuanNvbj9wYWdpbmc9ZmFsc2UmZmllbGRzPWlkLG5hbWUiKQ0KcHM8LWZyb21KU09OKGNvbnRlbnQoR0VUKHVybCksICJ0ZXh0IiksIGZsYXR0ZW4gPSBUUlVFKSAlPiUgDQogIGRhdGEuZnJhbWUoKSAlPiUgDQogIHNlbGVjdCgiaWQiPTIsICJwc25hbWUiPTEpDQpoZWFkKHBzKQ0KDQojT1UgbmFtZXMNCnVybDwtcGFzdGUwKGJhc2V1cmwsICJhcGkvb3JnYW5pc2F0aW9uVW5pdHMuanNvbj9wYWdpbmc9ZmFsc2UmZmllbGRzPWlkLG5hbWUiKQ0Kb3VfbmFtZXM8LWZyb21KU09OKGNvbnRlbnQoR0VUKHVybCksICJ0ZXh0IiksIGZsYXR0ZW4gPSBUUlVFKSAlPiUgDQogIGRhdGEuZnJhbWUoKSAlPiUgDQogIHNlbGVjdCgibmFtZSI9MSwgIm91X2lkIj0yKSAlPiUgDQogIG11dGF0ZSgib3VfdHlwZSI9Y2FzZV93aGVuKA0KICAgIHN0cl9kZXRlY3QobmFtZSwgIiBGV0MiKSB+ICJGV0MiLA0KICAgIHN0cl9kZXRlY3QobmFtZSwgIig/aSlDQyIpIH4gIkNDIiwNCiAgICBzdHJfZGV0ZWN0KG5hbWUsICJVbml0IikgfiAiVW5pdCIsDQogICAgc3RyX2RldGVjdChuYW1lLCAiV2FyZCIpIH4gIldhcmQiKSkNCg0KaGVhZChvdV9uYW1lcykNCmBgYA0KDQojIyBEYXRhIFByb2Nlc3NpbmcNCg0KV2UgdGhlbiBtZXJnZSB0aGUgcmF3IG91dHB1dHMgb2YgVEVJIGV2ZW50cyB3aXRoIG9yZyB1bml0IGluZm9ybWF0aW9uDQoNCklmIGFycmFuZ2UgZXZlbnRzIGJ5IHZpc2l0IG51bWJlciwgY2xhc3NpZnkgc3Vic2VxdWVudCB2aXNpdHMgYXMgYmVpbmcgYXQgc2FtZSBvciBkaWZmZXJlbnQgb3JnIHVuaXQgdGhhbiB0aGUgaW5pdGlhbCB2aXNpdC4NCg0KYGBge3IgcHJvY2Vzc2luZ30NCg0KbXlfZGF0YTwtcmF3NCAlPiUgDQogIGxlZnRfam9pbihvdV9uYW1lcywgYnk9YygiRXZ0T3JnVW5pdCI9Im91X2lkIikpICU+JSANCiAgc2VsZWN0KHRlaT0zLCBnYT04LCAib3VfaWQiPUV2dE9yZ1VuaXQsIG91X3R5cGUsIG5hbWUsIFZpc2l0Tm8sIEVuck9yZ1VuaXQsIFN0YWdlVWlkKSAlPiUgDQogIGRpc3RpbmN0KCkgJT4lICNldmVudCBtdXN0IGJlIHVuaXF1ZSB2aXNpdCwgaS5lLiBhbiBBTkMgbWFuYWdlbWVudCBhbmQgQU5DIHN0YWdlIHNhbWUgd2VlayB3b3VsZCBiZSBtZXJnZWQNCiAgbXV0YXRlKGdhPXJvdW5kKGFzLm51bWVyaWMoZ2EpKSkgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKG91X3R5cGUpICYgIWlzLm5hKGdhKSAmIGdhIDw9IDUwICYgZ2EgPj0gMSkgJT4lIA0KICBtdXRhdGUoVmlzaXRObz1pZl9lbHNlKFZpc2l0Tm8gPiA2LCAiNysiLCBhcy5jaGFyYWN0ZXIoVmlzaXRObykpKSAlPiUgDQogIG11dGF0ZShWaXNpdE5vPWFzLmZhY3RvcihWaXNpdE5vKSkgJT4lIA0KICBtdXRhdGUob3VfdHlwZT1yZWNvZGVfZmFjdG9yKG91X3R5cGUsICJGV0MiPSJGV0MiLCAiQ0MiPSJDQyIsICJXYXJkIj0iV2FyZCIsICJVbml0Ij0iVW5pdCIgKSkgJT4lIA0KICBncm91cF9ieSh0ZWkpICU+JSANCiAgYWRkX3RhbGx5KCkgJT4lIA0KICBhcnJhbmdlKHRlaSwgZ2EpICU+JSANCiAgbXV0YXRlKGZpcnN0ID0gZHBseXI6OmZpcnN0KG91X2lkKSkgJT4lDQogIG11dGF0ZShnYV9pbml0aWFsID0gZHBseXI6OmZpcnN0KGdhKSkgJT4lIA0KICBtdXRhdGUobGFzdF9vdSA9IGxhZyhvdV9pZCkpICU+JSANCiAgbXV0YXRlKE1vdmVkX291ID0gY2FzZV93aGVuKGZpcnN0ID09IG91X2lkICYgaXMubmEobGFzdF9vdSkgIH4gIkV2ZW50IDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgPT0gb3VfaWQgJiAhaXMubmEobGFzdF9vdSkgfiAiRXZlbnQgMissIHNhbWUgb3UgYXMgRXZlbnQgMSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgIT0gb3VfaWQgfiAiRXZlbnQgMissIGRpZmZlcmVudCBvdSBhcyBFdmVudCAxIikpICU+JSANCiAgbXV0YXRlKE1vdmVkX291X3dyYXAgPSBzdHJfd3JhcChNb3ZlZF9vdSwgd2lkdGggPSAyMCkpDQoNCiNteV9kYXRhDQoNCmhlYWQobXlfZGF0YSkNCg0KYGBgDQoNCiMgVmlzdWFsaXphdGlvbiANCg0KTGV0J3Mgc3RhcnQgc2ltcGxlIHdpdGggYSBoaXN0b2dyYW0uIEdBIG9mIGVhY2ggdmlzaXQsIGJ5IE9VIHR5cGUuDQoNCmBgYHtyIGhpc3QgYmFzaWN9DQpwPC1nZ3Bsb3QobXlfZGF0YSwgYWVzKGdhKSkrDQogIGdlb21faGlzdG9ncmFtKGJpbnM9MjUpKw0KICBmYWNldF93cmFwKH5vdV90eXBlLCBuY29sPTEpKw0KICBsYWJzKHRpdGxlPSJFdmVudHMgYnkgR0EgYW5kIE9VIHR5cGUiKQ0KcA0KDQpgYGANCg0KDQojIyBEZW5zaXR5IFBsb3QgYnkgT3JnIFVuaXQgdHlwZSBhbmQgR2VzdGF0aW9uYWwgQWdlDQoNCkFub3RoZXIgd2F5IHRvIHNob3cgdGhpcyBoaXN0b2dyYW0gaXMgYSBkZW5zaXR5IGRvdCBwbG90LiBUbyBtYWtlIGl0IGludGVyZXN0aW5nIHdlIGNhbiBhbmltYXRlIGl0LA0KdG8gZW1waGFzaXplIHRoaXMgcHJvZ3Jlc3Npb24gb2YgdGltZS4gSXQgbG9va3MgYSBiaXQgbGlrZSBhIHBhaW50IHJvbGxlci4uLg0KDQpgYGB7ciBkZW5zaXR5IGFuaW19DQoNCnA8LWdncGxvdChteV9kYXRhLCBhZXMoZ2EsIG91X3R5cGUpKSArDQogIGdlb21faml0dGVyKGFlcyhncm91cCA9IGdhLCBzaXplID0gLjMpLCBoZWlnaHQgPSAwLjI1LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGxhYnModGl0bGU9IlByZWduYW5jeSBldmVudHMgaW4gZS1SZWcgTWF0bGFiIGJ5IE9yZyBVbml0IiwNCiAgICAgICBzdWJ0aXRsZSA9ICdWaXNpdHMgYXQgR2VzdGF0aW9uYWwgQWdlIHtjbG9zZXN0X3N0YXRlfScsDQogICAgICAgeSA9ICdPcmcgVW5pdCBUeXBlJykgKw0KICMgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xfc2NhbGUpICsNCiAgdHJhbnNpdGlvbl9zdGF0ZXMoZ2EsIHRyYW5zaXRpb25fbGVuZ3RoID0gMywgc3RhdGVfbGVuZ3RoID0gMikgKw0KICBzaGFkb3dfbWFyayhzaXplID0gLjUpICsNCiAgZWFzZV9hZXMoJ2xpbmVhcicpDQoNCmFuaW1hdGUoDQogIHBsb3QgPSBwLCANCiAgbmZyYW1lcyA9IDIwMCwNCiAgZHVyYXRpb24gPSAxNSwNCiAgZW5kX3BhdXNlID0gNTANCikNCg0KDQoNCmBgYA0KDQpCdXQgdGhpcyBkb2VzbnQgc2F5IG11Y2ggYWJvdXQgcGF0aWVudCBtb3ZlbWVudC4gV2hlbiBkbyBjbGllbnRzIG1vdmUgdG8gYSBkaWZmZXJlbnQgb3JnIHVuaXQgY2xpbmljPw0KDQoqIFRoZSByZWQgZG90cyBhcmUgdGhlIEdBIGFuZCBsb2NhdGlvbiAob3JnIHVuaXQpIG9mIGZpcnN0IGV2ZW50IChpZGVudGlmaWNhdGlvbikuIA0KDQoqIFRoZSBibHVlIGRvdHMgYXJlIHN1YnNlcXVlbnQgZXZlbnRzIHRoYXQgYXJlIGF0IHNhbWUgbG9jYXRpb24gYXMgZmlyc3QgZXZlbnQuIA0KDQoqIFRoZSBncmVlbiBkb3RzIGFyZSBtb3ZlbWVudCB0byBhIERJRkZFUkVOVCBsb2NhdGlvbiB0aGFuIHRoZSBpZGVudGlmaWNhdGlvbiBvcmcgdW5pdC4NCg0KYGBge3IgZGVuc2l0eSB3aXRoIGNvbG9yc30NCnA8LWdncGxvdChteV9kYXRhLCBhZXMoZ2EsIE1vdmVkX291X3dyYXApKSArDQogIGdlb21faml0dGVyKGFlcyhncm91cCA9IGdhLCBjb2xvciA9IE1vdmVkX291X3dyYXApLCBzaXplID0gMC4wMSkgKw0KICBmYWNldF93cmFwKH5vdV90eXBlLCBuY29sID0gMSkrDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgbGFicyh0aXRsZT0iUHJlZ25hbmN5IGV2ZW50cyBpbiBlLVJlZyBNYXRsYWIgYnkgT3JnIFVuaXQiKSArDQogICAgeWxhYigiIikNCg0KcA0KDQpgYGANCg0KDQpTYW1lIHRoaW5nLCBidXQgYW5pbWF0ZWQuLi4NCg0KYGBge3IgYW5pbWF0ZWQgcGFpbnQgcm9sbGVyfQ0KcDwtcCArDQogIGxhYnModGl0bGU9IlByZWduYW5jeSBldmVudHMgaW4gZS1SZWcgTWF0bGFiIGJ5IE9yZyBVbml0IiwNCiAgICAgICBzdWJ0aXRsZSA9ICdWaXNpdHMgYXQgR2VzdGF0aW9uYWwgQWdlIHtjbG9zZXN0X3N0YXRlfScpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHRyYW5zaXRpb25fc3RhdGVzKGdhLCB0cmFuc2l0aW9uX2xlbmd0aCA9IDMsIHN0YXRlX2xlbmd0aCA9IDIpICsNCiAgc2hhZG93X21hcmsoc2l6ZSA9IC4zKSArDQogIGVhc2VfYWVzKCdsaW5lYXInKQ0KDQphbmltYXRlKA0KICBwbG90ID0gcCwgDQogIG5mcmFtZXMgPSAyMDAsDQogIGR1cmF0aW9uID0gMTUsDQogIGVuZF9wYXVzZSA9IDUwDQopDQoNCg0KYGBgDQoNCg0KDQpOb3cgd2UgY2FuIHNlZSB0aGF0IG1vc3QgbW92ZW1lbnQgdG8gbmV3IHBsYWNlIGhhcHBlbnMgYWZ0ZXIgMzYgd2Vla3MgZm9yIEZXQSB1bml0cyBhbmQgSEEgV2FyZHMgKEhvbWUgUFBDIGZvbGxvdyB1cCBmb3IgdGhvc2Ugbm90IGlkZW50aWZpZWQgYnkgRldBKS4gQnV0IHdvbWVuIG1vdmUgdG8gRldDIGJldHdlZW4gMTQgYW5kIDM1IHdlZWtzLiBGZXdlciB3b21lbiBjaG9vc2UgdG8gbW92ZSB0byBDQywgaWYgdGhleSB3ZXJlIGlkZW50aWZpZWQgZWxzZXdoZXJlLg0KDQoNCldlIHdhbnQgdG8gbmFycm93IGluIG9uIHRoZSBwYXRpZW50cyB3aG8gc3RhcnQgYXQgb25lIG9yZyB1bml0LCBhbmQgcmVjZWl2ZSBzZXJ2aWNlcyBhdCBhbm90aGVyLiBUaGlzIGFwcHJvYWNoIHRlbGxzIHVzIHdoYXQga2luZCBvZiBvcmcgdW5pdCB0aGV5IEdPIHRvLCBidXQgbm90IHdoYXQga2luZCBvZiBvcmcgdW5pdCB0aGV5IENPTUUgZnJvbS4NCg0KIyMgQ2hvcmQgZGlhZ3JhbQ0KW0Nob3JkIGRpYWdyYW1dKCBodHRwczovL2Jvc3Qub2Nrcy5vcmcvbWlrZS91YmVyZGF0YS8pIG1pZ2h0IGJlIGhlbHBmdWwgdG9vIGhlcmUgLSB0byBkby4NCg0KDQoNCiMjIEhlYXQgTWFwDQoNCklmIHdlIG5hcnJvdyBpbiBvbiB0aGUgcGF0aWVudHMgd2hvIG1vdmUgdG8gYSBuZXcgbG9jYXRpb24gKHRoZSBncmVlbiBkb3RzIGFib3ZlKSwgd2UgY2FuIHNlZSB0aGUgb3ZlcmxhcCBvZiBzZXJ2aWNlIHByb3Zpc2lvbiBiZXR3ZWVuIHR5cGVzIG9mIG9yZyB1bml0Lg0KDQpIZXJlIGlzIGEgdGFibGUgb2YgZXZlbnRzIHRoYXQgYXJlIGF0IGEgKmRpZmZlcmVudCBvcmcgdW5pdCogdGhhbiB0aGUgZW5yb2xsbWVudCBvcmcgdW5pdCwgYXJyYW5nZWQgYnkgb3JnIHVuaXQgdHlwZS4NCg0KYGBge3IgcGNvcmRzIGRhdGF9DQpwY29yZHNfZGF0YSA8LW15X2RhdGEgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBmaWx0ZXIoTW92ZWRfb3U9PSJFdmVudCAyKywgZGlmZmVyZW50IG91IGFzIEV2ZW50IDEiKSAlPiUgDQogIHJlbmFtZSgiZXZlbnRfb3VfaWQiPW91X2lkLCAiZXZlbnRfb3VfdHlwZSI9b3VfdHlwZSkgJT4lIA0KICBsZWZ0X2pvaW4ob3VfbmFtZXMsIGJ5PWMoIkVuck9yZ1VuaXQiPSJvdV9pZCIpKSAlPiUgDQogIHJlbmFtZSgiZW5yX291X3R5cGUiPW91X3R5cGUpICU+JQ0KICBzZWxlY3QoZW5yX291X3R5cGUsIGV2ZW50X291X3R5cGUsICJldmVudF9nYSI9Z2EsICJlbnJfZ2EiPWdhX2luaXRpYWwpIA0KDQoNCnRlc3Q8LXBjb3Jkc19kYXRhICU+JQ0KICBhcnJhbmdlKGV2ZW50X291X3R5cGUpICU+JSANCiAgbXV0YXRlKGV2ZW50X291X3R5cGU9ZmFjdG9yKGV2ZW50X291X3R5cGUsIGxldmVscz1jKCJDQyIsIkZXQyIsIlVuaXQiLCJXYXJkIikpKSAlPiUgDQogIGdyb3VwX2J5KGVucl9vdV90eXBlLCBldmVudF9vdV90eXBlKSAlPiUgDQogIHNlbGVjdCgiZW5yb2xsbWVudCBPVSI9ZW5yX291X3R5cGUsICJldmVudCBPVSI9ZXZlbnRfb3VfdHlwZSkgJT4lIA0KICBzdW1tYXJpemUoY291bnQ9bigpKQ0KDQprYWJsZSh0ZXN0KSAlPiUNCiAga2FibGVfc3R5bGluZygpDQoNCg0KYGBgDQoNCg0KV2UgY2FuIHZpc3VhbGl6ZSB0aGlzIGdyYXBoIGluIGEgaGVhdG1hcA0KDQpgYGB7ciBoZWF0bWFwIDF9DQojIEdpdmUgZXh0cmVtZSBjb2xvcnM6DQpsaWJyYXJ5KHZpcmlkaXMpDQoNCmdncGxvdCh0ZXN0LCBhZXMoYGVucm9sbG1lbnQgT1VgLCBgZXZlbnQgT1VgLCBmaWxsPSBjb3VudCkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPUZBTFNFKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KV2UgY2FuIGZhY2V0IHRoZXNlIGRvd24gYnkgZW5yb2xsbWVudCBPVSwgdGhlbiBzaG93IHRoZSBldmVudCBHQSBmb3IgZWFjaCBzdWJzZXF1ZW50IGV2ZW50Lg0KDQpgYGB7ciBoZWF0bWFwIDJ9DQoNCmhlYXQyPC1wY29yZHNfZGF0YSAlPiUNCiAgICBtdXRhdGUoZXZlbnRfb3VfdHlwZT1mYWN0b3IoZXZlbnRfb3VfdHlwZSwgbGV2ZWxzPWMoIkNDIiwiRldDIiwiVW5pdCIsIldhcmQiKSkpICU+JSANCiAgbXV0YXRlKGdlc3RhZ2VfZXZlbnQ9Y2FzZV93aGVuKA0KICAgIGV2ZW50X2dhID49IDAgJiBldmVudF9nYSA8IDE4IH4gIjAtMTciLA0KICAgIGV2ZW50X2dhID49IDE4ICYgZXZlbnRfZ2EgPCAyNCB+ICIxOC0yMyIsDQogICAgZXZlbnRfZ2EgPj0gMjQgJiBldmVudF9nYSA8IDI5IH4gIjI0LTI5IiwNCiAgICBldmVudF9nYSA+PSAyOSAmIGV2ZW50X2dhIDwgMzQgfiAiMjktMzMiLA0KICAgIGV2ZW50X2dhID49IDM0ICYgZXZlbnRfZ2EgPCA0MCB+ICIzNC0zOSIsDQogICAgICAgICAgICAgICAgICAgICBldmVudF9nYSA+PSA0MCB+ICI0MCsiLA0KICApKSAlPiUgDQogIGdyb3VwX2J5KGVucl9vdV90eXBlLCBldmVudF9vdV90eXBlLCBnZXN0YWdlX2V2ZW50KSAlPiUgDQogIHNlbGVjdCgiZW5yb2xsbWVudCBPVSI9ZW5yX291X3R5cGUsICJldmVudCBPVSI9ZXZlbnRfb3VfdHlwZSwgZ2VzdGFnZV9ldmVudCkgJT4lIA0KICBzdW1tYXJpemUoY291bnQ9bigpKQ0KDQoNCmdncGxvdChoZWF0MiwgYWVzKGdlc3RhZ2VfZXZlbnQsIGBldmVudCBPVWAsIGZpbGw9IGNvdW50KSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9RkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+YGVucm9sbG1lbnQgT1VgLGxhYmVsbGVyID0gImxhYmVsX2JvdGgiKSsNCiAgbGFicyh0aXRsZT0iRXZlbnRzIGF0IGRpZmZlcmVudCBPVSB0aGFuIGVucm9sbG1lbnQiLA0KICAgICAgIHN1YnRpdGxlPSJCeSBFdmVudCBHZXN0IEFnZSBhbmQgT1UgVHlwZSIpDQoNCmBgYA0KDQoNCiMjIEFuaW1hdGVkIERvdCBQbG90DQoNCg0KV2hhdCBhcmUgdGhlIHBhdHRlcm5zIG92ZXIgdGltZSB0aG91Z2g/IA0KDQpJbiB0aGlzIGFuaW1hdGlvbiwgd2UgZm9jdXMgb24gdGhlICJncmVlbiBkb3RzIiBhYm92ZS4gRWFjaCBncmVlbiBkb3QgaXMgYW4gZXZlbnQuIFRoZSBob3Jpem9udGFsIGxpbmVzIHJlcHJlc2VudCBhIHR5cGUgb2Ygb3JnIHVuaXQuIFRoZSBtaWRkbGUgZ3JleSBkb3RzIHJlcHJlc2VudCB0aGUgaWRlbnRpZmljYXRpb24gZXZlbnRzLS1vbmNlIHRoZXkgY3Jvc3MgdGhhdCBkb3QsIHRoZSBwcmVnbmFuY3kgaXMgaWRlbnRpZmllZC4NCg0KQWZ0ZXIgdGhhdCwgY2xpZW50cyBnbyB0byBtYW55IG90aGVyIHR5cGVzIG9mIG9yZyB1bml0cy4gU29tZSBnbyB0byBhIGRpZmZlcmVudCBvcmcgdW5pdCBvZiBzYW1lIHR5cGUsIHdoaWxlIG90aGVycyBnbyB0byBhIG5ldyBvcmcgdW5pdCB0eXBlLg0KDQpCeSBhbmltYXRpbmcgdGhpcyBvdmVyIGdlc3RhdGlvbmFsIGFnZSBhdCB2aXNpdCwgd2UgY2FuIHNlZSB3aGljaCB3ZWVrcyBoYWQgaGlnaGVzdCAiY3Jvc3NvdmVyIiBvZiBldmVudHMuIFRoZSBzcGVlZCBvZiBkb3QgbW92ZW1lbnQgcmVwcmVzZW50cyB0aGUgdGltZSBiZXR3ZWVuIHZpc2l0cy4gDQoNCmBgYHtyIGFuaW1hdGlvbn0NCg0KcGNvcmQyPC1wY29yZHNfZGF0YSAlPiUgDQpyb3duYW1lc190b19jb2x1bW4odmFyPSJpZCIpICU+JSANCm11dGF0ZSgiZW5yX3N0YXJ0Ij1lbnJfb3VfdHlwZSkgJT4lIA0KcGl2b3RfbG9uZ2VyKGMoJ2Vucl9zdGFydCcsICdlbnJfb3VfdHlwZScsJ2V2ZW50X291X3R5cGUnKSwgDQogICAgICAgICAgICAgbmFtZXNfdG8gPSAic3RhcnRfZmluaXNoIiwgDQogICAgICAgICAgICAgdmFsdWVzX3RvPSJvdV90eXBlIikgJT4lIA0KICBtdXRhdGUoZ2VzdGFnZT1pZl9lbHNlKHN0YXJ0X2ZpbmlzaD09ImVucl9vdV90eXBlIiwgZW5yX2dhLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShzdGFydF9maW5pc2g9PSJldmVudF9vdV90eXBlIiwgZXZlbnRfZ2EsIDApKSkgJT4lIA0KICBtdXRhdGUoZW5kcG9pbnQ9aWZfZWxzZShzdGFydF9maW5pc2g9PSJlbnJfc3RhcnQiLCAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKHN0YXJ0X2ZpbmlzaD09ImVucl9vdV90eXBlIiwgMSwgMikpKSAlPiUgDQogIG11dGF0ZShvdV90eXBlPWZhY3RvcihvdV90eXBlLCBsZXZlbHMgPSBjKCJVbml0IiwiV2FyZCIsIkZXQyIsIkNDIikpKSAlPiUgDQogIG11dGF0ZShnZXN0YWdlPWlmX2Vsc2UoZW5kcG9pbnQ9PTIgJiBlbnJfZ2EgPiAzMCAmIGdlc3RhZ2UgPiAzMCwgZ2VzdGFnZSArIDIsIGdlc3RhZ2UpKSAlPiUgDQogIG11dGF0ZShnZXN0YWdlPWlmX2Vsc2UoZW5kcG9pbnQ9PTIsIGdlc3RhZ2UgKyAxLCBnZXN0YWdlKSkNCg0KDQpwY29yZF9zdW1tPC1wY29yZDIgJT4lIA0KICBmaWx0ZXIoZW5kcG9pbnQhPTApICU+JSANCiAgZ3JvdXBfYnkoZW5kcG9pbnQsIG91X3R5cGUsIGdlc3RhZ2UpICU+JSANCiAgc3VtbWFyaXNlKCJjb3VudCI9bl9kaXN0aW5jdChpZCkpICU+JSANCiAgbXV0YXRlKCJjdW1zdW0iPWN1bXN1bShjb3VudCkpDQoNCiNwY29yZDIgJT4lIGZpbHRlcihlbmRwb2ludD09MiAmIGdlc3RhZ2UgPiAzNSkNCg0KDQpwczE8LXBjb3JkX3N1bW0gJT4lIGZpbHRlcihlbmRwb2ludD09MSkNCnBzMjwtcGNvcmRfc3VtbSAlPiUgZmlsdGVyKGVuZHBvaW50PT0yKQ0KDQpwMjwtZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBwY29yZDIsIGFlcyh4PWVuZHBvaW50LCB5ID0gb3VfdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gaWQpLCBjb2wgPSAiZ3JlZW4iKSAgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBwczEsIGFlcyh4PWVuZHBvaW50LCB5ID0gb3VfdHlwZSwgc2l6ZSA9IGN1bXN1bSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD0iZ3JleSIsIGFscGhhPTAuOCkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBwczIsIGFlcyh4PWVuZHBvaW50LCB5ID0gb3VfdHlwZSwgc2l6ZSA9IGN1bXN1bSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPSJncmV5IiwgYWxwaGE9MC44KSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0cmFuc2l0aW9uX3JldmVhbChnZXN0YWdlKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9YygwLCAxLCAyKSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiR0EgMCIsIklkZW50aWZpY2F0aW9uIiwgIk90aGVyIEV2ZW50IikpICsNCiAgbGFicyh0aXRsZT0iZS1SZWcgTWF0bGFiIC0tIEV2ZW50cyBhdCBEaWZmZXJlbnQgT3JnIFVuaXQgdGhhbiBJZGVudGlmaWNhdGlvbiIsDQogICAgICAgc3VidGl0bGUgPSAnRXZlbnRzIGF0IEdlc3RhdGlvbmFsIEFnZSB7cm91bmQoZnJhbWVfYWxvbmcpfScsDQogICAgICAgeCA9ICIiKQ0KDQphbmltYXRlKHAyLA0KICAgICAgICBuZnJhbWVzID0gMjAwLA0KICAgICAgICBkdXJhdGlvbiA9IDE1LA0KICAgICAgICBlbmRfcGF1c2UgPSA1MCkNCmBgYA0KDQpJZiBhIGRvdCBtb3ZlcyBxdWlja2x5IGJldHdlZW4gaWRlbnRpZmljYXRpb24gYW5kIHN1YnNlcXVlbnQgdmlzaXQsIHRoYXQgbWVhbnMgdGhhdCB0aGUgbmV4dCB2aXNpdCBoYXBwZW5lZCBxdWlja2x5IGFmdGVyIGlkZW50aWZpY2F0aW9uIChlLmcuLCBVbml0IGlkZW50aWZpZXMgcHJlZ25hbmN5IGF0IDE2IHdlZWtzLCB2aXNpdCB0byBDSENQIGF0IDE4IHdlZWtzKS4gSW52ZXJzZWx5LCB0aGUgc2Vjb25kIHZpc2l0IG1heSBvY2N1ciBsb25nIGFmdGVyIGlkZW50aWZpY2F0aW9uIChzYW1lIGNsaWVudCB2aXNpdHMgQ0hDUCBhZ2FpbiBhdCAzMiB3ZWVrcywgdGhpcyB3b3VsZCBiZSBhIHNsb3dlciBtb3ZpbmcgZG90KS4NCg0KRXZlbnR1YWxseSB3YW50IHRvIHJlY3JlYXRlIGFzIGEgW1NhbmtleSBmbG93IGRpYWdyYW1dKGh0dHBzOi8vd3d3Lmh2aXRmZWxkdC5tZS9ibG9nL3JlY3JlYXRlLXNhbmtleS1mbG93LWNoYXJ0LykgYWNyb3NzIDQgQU5DIHZpc2l0cy4gU2VlIGJlbG93Lg0KDQoNCg0KIyMgUGFyYWxsZWwgQ29vcmRpbmF0ZXMNCg0KSWYgd2Ugd2FudCB0byBleHBsb3JlIHRoZXNlIHJlbGF0aW9uc2hpcHMgZnVydGhlciwgd2UgY2FuIHVzZSBpbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9uLg0KDQpUaGUgdHlwZSBiZWxvdyBpcyBjYWxsZWQgKnBhcmFsbGVsIGNvb3JkaW5hdGVzLiogDQoNClRoZSB2ZXJ0aWNhbCBheGVzIHJlcHJlc2VudCB2YXJpYWJsZXMuIFRoZSBob3Jpem9udGFsIGFuZCBkaWFnb25hbCBsaW5lcyBhcmUgb2JzZXJ2YXRpb25zLCB3aGVyZSBlYWNoIGlzIGFuIEVWRU5UIHRoYXQgdG9vayBhdCBhIGRpZmZlcmVudCBwbGFjZSB0aGFuIHRoZSBlbnJvbGxtZW50IG9yZyB1bml0LiBUaGUgY29sb3JzIGFyZSBiYXNlZCBvbiB0aGUgdHlwZSBvZiBlbnJvbGxtZW50IG9yZyB1bml0Lg0KDQpZb3UgY2FuIGNsaWNrIGFuZCBkcmFnIGFjcm9zcyBhbiBheGlzIHRvIHNlbGVjdCBhIHJhbmdlIGZvciBlYWNoIHZhcmlhYmxlLCBhbmQgaXQgd2lsbCBmaWx0ZXIgZG93biB0byB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgbWVldCB0aGF0IGNyaXRlcmlhLiBGb3IgZXhhbXBsZSwgb2YgdGhvc2UgY2xpZW50cyB3aG8gd2VyZSBlbnJvbGxlZCBhdCBhIFdBUkQsIGFuZCBsYXRlciB3ZW50IHRvIGEgQ0MsIHdoYXQgd2FzIHRoZSByYW5nZSBvZiBnZXN0YXRpb25hbCBhZ2VzIHdoZW4gdGhhdCBDQyB2aXNpdCB0b29rIHBsYWNlPw0KDQpgYGB7ciBwYXJjb29yZHN9DQojIyMjIyBQYXJhbGxlbCBDb29yZGluYXRlcyBHcmFwaCAjIyMjIyMNCmxpYnJhcnkocGFyY29vcmRzKQ0KI3BhcmFsbGVsIGNvb3JkaW5hdGVzIHdpdGggY29sb3IgYmFzZWQgb24gZ2VuZGVyDQoNCnBhcmNvb3Jkczo6cGFyY29vcmRzKGRhdGEgPSBwY29yZHNfZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbGlzdCgNCiAgICAgICAgICAgICAgICAgICAgICAgIyBkaXNjcmV0ZSBvciBjYXRlZ29yaWNhbCBjb2x1bW4NCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3JTY2FsZSA9ICJzY2FsZU9yZGluYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICBjb2xvckJ5ID0gImVucl9vdV90eXBlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3JTY2hlbWUgPSAic2NoZW1lQ2F0ZWdvcnkxMCIpLA0KICAgICAgICAgICAgICAgICAgICAgd2l0aEQzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIGJydXNoTW9kZSA9ICIxRC1heGVzLW11bHRpIiwNCiAgICAgICAgICAgICAgICAgICAgIGFscGhhT25CcnVzaGVkID0gMC4yLA0KICAgICAgICAgICAgICAgICAgICAgcXVldWUgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgcmF0ZSA9IDUwLA0KICAgICAgICAgICAgICAgICAgICAgcmVvcmRlcmFibGUgPSBUUlVFKQ0KDQoNCmBgYA0KDQpUbyBtYWtlIHRoZSBjb3JyZWxhdGlvbnMgZWFzaWVyIHRvIHJlYWQsIHlvdSBjYW4gZHJhZyBhbmQgcmVhcnJhbmdlIHRoZSBheGVzIG9yZGVyLg0KDQpXZSBtaWdodCBleHBhbmQgb24gdGhpcyBieSBsaW5raW5nIHRvIHRoZSBzZWxlY3RlZCByYW5nZXMgdG8gdGFibGVzLCBmb3IgZHluYW1pYyBmaWx0ZXJpbmcgb2YgZGF0YS4NCg0KDQoNCiMgRHJvcG91dHM6IElzb2xhdGluZyBDbGllbnRzIHdpdGggb25seSBvbmUgZXZlbnQNCg0KSWYgdGhlIGNsaWVudCBoYXMgb25seSBvbmUgZXZlbnQgaW4gc3lzdGVtLCBtYXliZSB0aGV5IGFyZSBkaWZmZXJlbnQgZm9yIHNvbWUgcmVhc29uIHRoYW4gdGhlIG90aGVyIGV2ZW50cy4NCg0KRm9yIGV4YW1wbGUsIHdoYXQgc3RhZ2Ugd2FzIHRoZWlyIG9ubHkgZXZlbnQ/DQoNCldoYXQga2luZCBvZiBvcmcgdW5pdD8NCg0KVGhlIGJlbG93IHRhYmxlcyBhcmUgb25seSBvbmUgZXZlbnQuIA0KDQpGaXJzdCBpcyBieSBvcmcgdW5pdCAtLSBtb3N0IG9mIHRoZXNlIGFyZSB0aGUgUHJlZ25hbmN5IElEIHN0YWdlLg0KDQpgYGB7ciBrYWJsZX0NCmxpYnJhcnkoa2FibGVFeHRyYSkNCiMjI0lzb2xhdGUgdGhvc2UgdGhhdCBvbmx5IGhhdmQgb25lIGV2ZW50DQpteV9kYXRhX2lzbzwtcmF3NCAlPiUgDQogIGxlZnRfam9pbihvdV9uYW1lcywgYnk9YygiRXZ0T3JnVW5pdCI9Im91X2lkIikpICU+JSANCiAgc2VsZWN0KHRlaT0zLCBnYT04LCAib3VfaWQiPUV2dE9yZ1VuaXQsIG91X3R5cGUsIG5hbWUsIFZpc2l0Tm8sIEVuck9yZ1VuaXQsIFN0YWdlVWlkKSAlPiUNCiAgbGVmdF9qb2luKHBzLCBieSA9IGMoIlN0YWdlVWlkIj0iaWQiKSkgJT4lIA0KICBtdXRhdGUoZ2E9cm91bmQoYXMubnVtZXJpYyhnYSkpKSAlPiUgDQogIGZpbHRlcighaXMubmEob3VfdHlwZSkgJiAhaXMubmEoZ2EpICYgDQogICAgICAgICAgIGdhIDw9IDUwICYgZ2EgPj0gMSAmDQogICAgICAgICAgc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoInJlZ25hbmMiLCAiQU5DIiwgIk5ld2Jvcm4iLCJQTkMiLCJMYWIiKSxjb2xsYXBzZSA9ICd8JykpICYNCiAgICAgICAgICAhc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoIlByZXYiLCJSaXNrIiwiTWFuYWciKSxjb2xsYXBzZSA9ICd8JykpKSAlPiUgDQogIGdyb3VwX2J5KHRlaSkgJT4lIA0KICBhZGRfdGFsbHkoKSAlPiUgDQogIGFycmFuZ2UodGVpLCBnYSkgJT4lIA0KICBtdXRhdGUoZmlyc3QgPSBkcGx5cjo6Zmlyc3Qob3VfaWQpKSAlPiUNCiAgbXV0YXRlKGxhc3Rfb3UgPSBsYWcob3VfaWQpKSAlPiUNCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKE1vdmVkX291ID0gY2FzZV93aGVuKGZpcnN0ID09IG91X2lkICYgaXMubmEobGFzdF9vdSkgIH4gIkV2ZW50IDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgPT0gb3VfaWQgJiAhaXMubmEobGFzdF9vdSkgfiAiRXZlbnQgMissIHNhbWUgb3UgYXMgRXZlbnQgMSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgIT0gb3VfaWQgfiAiRXZlbnQgMissIGRpZmZlcmVudCBvdSBhcyBFdmVudCAxIikpDQoNCiMgbXlfZGF0YV9pc28gJT4lIA0KIyAgIGdyb3VwX2J5KG91X3R5cGUsIG4pICU+JSANCiMgICBzdW1tYXJpc2UoImV2ZW50cyI9IG4oKSkgJT4lIA0KIyAgIG11dGF0ZShwZXJjZW50ID0gcm91bmQoZXZlbnRzL3N1bShldmVudHMpLCAyKSkNCg0KI215X2RhdGFfaXNvDQoNCnRlc3QyPC1teV9kYXRhX2lzbyAlPiUgDQogIGdyb3VwX2J5KHRlaSkgJT4lIA0KICBmaWx0ZXIobj09MSkgJT4lIA0KICBncm91cF9ieShwc25hbWUpICU+JSANCiAgc3VtbWFyaXplKCJzdGFnZV9jb3VudCI9bigpKQ0KDQp0ZXN0MzwtbXlfZGF0YV9pc28gJT4lIA0KICBmaWx0ZXIobj09MSkgJT4lIA0KICBncm91cF9ieShvdV90eXBlKSAlPiUgDQogIHN1bW1hcml6ZSgib3VfdHlwZV9jb3VudCI9bigpKQ0KDQoja2FibGVFeHRyYTo6a2FibGUodGVzdDIpDQprYWJsZSh0ZXN0MikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCg0KYGBgDQoNCk5leHQgaXMgYnkgb3JnIHVuaXQuIE1vc3Qgb2YgdGhlc2UgYXJlIHRoZSBGV0EgVW5pdHMuDQoNCmBgYHtyfQ0Ka2FibGUodGVzdDMpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCg0KDQpgYGANCg0KIyBDYXNjYWRlIGJ5IFZpc2l0IE51bWJlcg0KDQpUaGlzIHNlY3Rpb24gc2hvd3MgKkFMTCBWSVNJVFMqIGZvciBlYWNoIHBhdGllbnQgV2UgY2FuIHZpc3VhbGl6ZSB0aGUgdmlzaXQgbnVtYmVyICh4IGF4aXMpLCBHQSBhdCB2aXNpdCAodGltZSksIGFuZCB0eXBlIG9mIG9yZyB1bml0IGF0IGVhY2ggdmlzaXQgKHkgYXhpcykuIFRoZSBpbnNwaXJhdGlvbiBpcyB0aGlzIFtOWVRpbWVzIGluZm9ncmFwaGljXShodHRwczovL3d3dy5ueXRpbWVzLmNvbS9pbnRlcmFjdGl2ZS8yMDE4LzAzLzE5L3Vwc2hvdC9yYWNlLWNsYXNzLXdoaXRlLWFuZC1ibGFjay1tZW4uaHRtbCkNCg0KRWFjaCBkb3QgcmVwcmVzZW50cyBhIHBhdGllbnQgYXMgc2hlIG1vdmVzIHRocm91Z2ggZWFjaCBsZXZlbCBvZiB0aGUgaGVhbHRoIHN5c3RlbS4gVGhlIHBhdGllbnQncyBkb3QgInJlc3RzIiBhdCB0aGUgbG9jYXRpb24gb2YgdGhlIGxhc3QgdmlzaXQgcmVjb3JkZWQuIE5vdGUgdGhhdCB0aGUgdGhpcyBjb25zaWRlcnMgZWFjaCBpZGVudGlmaWNhdGlvbiwgQU5DIHZpc2l0LCBvciBob21lIHZpc2l0IHN0YWdlIGEgc2VwYXJhdGUgInZpc2l0Ii4gDQoNCllvdSBjYW4gc2VlIHRoYXQgdmVyeSBmZXcgcGF0aWVudHMgZ2V0IHBhc3QgdGhlIDNyZCB2aXNpdCBhdCBhbnkgbGV2ZWwuIE1vc3Qgb2YgdGhlIG1pZ3JhdGlvbiBvY2N1cnMgZnJvbSB0aGUgRldBIFVuaXQgbGV2ZWwgdXAgdGhlIHN5c3RlbSB0byBGV0MuIEFuZCBjb21wYXJlZCB0byBDQyBsZXZlbCwgbW9yZSBjbGllbnRzIHdobyBtYWRlIGl0IHRvIEZXQyBieSB0aGUgdGhpcmQgdmlzaXQgc3RhcnRlZCBmcm9tIGFub3RoZXIgb3JnIHVuaXQgKHJlZCBkb3RzKS4NCg0KYGBge3J9DQoNCmNhc2NhZGVfZGF0YSA8LW15X2RhdGEgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICByZW5hbWUoImV2ZW50X291X2lkIj1vdV9pZCwgImV2ZW50X291X3R5cGUiPW91X3R5cGUpICU+JSANCiAgc2VsZWN0KHRlaSwgZXZlbnRfb3VfdHlwZSwgImV2ZW50X2dhIj1nYSwgTW92ZWRfb3UsIFN0YWdlVWlkKSAlPiUgDQogIGxlZnRfam9pbihwcywgYnkgPSBjKCJTdGFnZVVpZCI9ImlkIikpICU+JSANCiAgZmlsdGVyKCFpcy5uYShldmVudF9vdV90eXBlKSAmICFpcy5uYShldmVudF9nYSkgJiANCiAgICAgICAgICAgZXZlbnRfZ2EgPD0gNTAgJiBldmVudF9nYSA+PSAxICYNCiAgICAgICAgICBzdHJfZGV0ZWN0KHBzbmFtZSwgcGFzdGUoYygicmVnbmFuYyIsICJBTkMiKSxjb2xsYXBzZSA9ICd8JykpICYNCiAgICAgICAgICAhc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoIlByZXYiLCJSaXNrIiwiTWFuYWciLCAiT3V0IiksY29sbGFwc2UgPSAnfCcpKSkgJT4lIA0KICBncm91cF9ieSh0ZWkpICU+JSANCiAgbXV0YXRlKCJWaXNpdE5vIj1pZl9lbHNlKHN0cl9kZXRlY3QocHNuYW1lLCAiaWRlbnQiKSwgMSwgMikpICU+JSANCiAgYXJyYW5nZShWaXNpdE5vLCBldmVudF9nYSkgJT4lIA0KICBtdXRhdGUoIlZpc2l0Tm8iPXJvd19udW1iZXIoKSkgDQoNCmNhc2NhZGVfZGF0YTwtY2FzY2FkZV9kYXRhICU+JSANCiAgbXV0YXRlKCJWaXNpdE5vIj1pZl9lbHNlKFZpc2l0Tm89PTEsIDAsIGFzLmRvdWJsZShWaXNpdE5vKSkpICU+JSANCiAgYmluZF9yb3dzKGNhc2NhZGVfZGF0YSAlPiUgIGZpbHRlcihWaXNpdE5vID09IDEpKSAlPiUgDQogIG11dGF0ZSgiTW92ZWRfb3UiPWlmX2Vsc2UoVmlzaXRObyA8PTEsICJFdmVudCAxIiwgTW92ZWRfb3UpKSAlPiUgDQogIG11dGF0ZShldmVudF9vdV90eXBlPWZhY3RvcihldmVudF9vdV90eXBlLCBsZXZlbHMgPSBjKCJVbml0IiwiV2FyZCIsIkZXQyIsIkNDIikpKSAlPiUgDQogIG11dGF0ZShncm91cGlkPSBncm91cF9pbmRpY2VzKCkpICU+JSANCiAgYXJyYW5nZSh0ZWksIFZpc2l0Tm8pICU+JSANCiAgbXV0YXRlKGdlc3RhZ2U9aWZfZWxzZShWaXNpdE5vPT0wLCAwLCBldmVudF9nYSkpICU+JSANCiAgZmlsdGVyKFZpc2l0Tm88OCkgJT4lIA0KICBtdXRhdGUoZ2VzdGFnZT1pZl9lbHNlKGdlc3RhZ2U9PWxhZyhnZXN0YWdlKSAmIGdyb3VwaWQ9PWxhZyhncm91cGlkKSAmIGdlc3RhZ2UhPTAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGdlc3RhZ2UrMiwgZ2VzdGFnZSkpDQoNCg0KDQpwMzwtZ2dwbG90KCkgKw0KICBnZW9tX2ppdHRlcihkYXRhID0gY2FzY2FkZV9kYXRhLCBhZXMoeD1WaXNpdE5vLCB5ID0gZXZlbnRfb3VfdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXBpZCwgY29sID0gTW92ZWRfb3UpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT0wLjUsIHdpZHRoID0gMC4xNSwgaGVpZ2h0PTAuMSkgICsNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHRyYW5zaXRpb25fcmV2ZWFsKGdlc3RhZ2UpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDA6NyksDQogICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkdBIDAiLCAiRXZlbnQxIiwgIkV2ZW50MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV2ZW50MyIsIkV2ZW50NCIsIkV2ZW50NSIsICJFdmVudDYiLCJFdmVudDciKSwNCiAgICAgICAgICAgICAgICAgICBtaW5vcl9icmVha3MgPSBOVUxMKSArDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiZ3JleSIsICJyZWQiLCAiZGFya2JsdWUiKSkgKw0KICBsYWJzKHRpdGxlPSJlLVJlZyBNYXRsYWIgLS0gRXZlbnRzIDEtNyIsDQogICAgICAgc3VidGl0bGUgPSAnRXZlbnRzIGF0IEdlc3RhdGlvbmFsIEFnZSB7cm91bmQoZnJhbWVfYWxvbmcpfScsDQogICAgICAgeCA9ICIiLA0KICAgICAgIHk9IiIpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCg0KDQphbmltYXRlKHAzLA0KICAgICAgICBuZnJhbWVzID0gMjAwLA0KICAgICAgICBkdXJhdGlvbiA9IDE1LA0KICAgICAgICBlbmRfcGF1c2UgPSA1MCkNCg0KI2dnYW5pbWF0ZTo6YW5pbV9zYXZlKCJiZF92aXNpdDF0bzcuZ2lmIikNCg0KDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQo=